﻿using Artech.Common;
using Artech.Core.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ContainerModel;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ContainerModel.Unity;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using Microsoft.Practices.Unity.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Web.Compilation;

namespace Artech.Core
{
    /// <summary>
    /// The factory to create or get service locator.
    /// </summary>
    public  class ServiceLocatorBuilder
    {
        private static ServiceLocatorBuilder current;
        private Dictionary<IConfigurationSource, IUnityContainer> unityContainers;
        private object syncHelper = new object();
        private Func<IConfigurationSource, IUnityContainer> unityContainerAccessor;
        private ServiceLocatorBuilder() 
        {
            unityContainers = new Dictionary<IConfigurationSource, IUnityContainer>();
            unityContainerAccessor = configSource =>
                {
                    IUnityContainer container;
                    if (unityContainers.TryGetValue(configSource, out container))
                    {
                        return container;
                    }
                    lock (syncHelper)
                    {
                        if (unityContainers.TryGetValue(configSource, out container))
                        {
                            return container;
                        }
                        container = UnityContainerBuilder.Current.CreateDefaultUnityContainer(configSource);
                        unityContainers.Add(configSource, container);
                        return container;
                    }
                };
        }

        /// <summary>
        /// Gets the current <see cref="ServiceLocatorBuilder"/>.
        /// </summary>
        /// <value>
        /// The current <see cref="ServiceLocatorBuilder"/>.
        /// </value>
        public static ServiceLocatorBuilder Current
        {
            get { return current ?? (current = new ServiceLocatorBuilder()); }
        }

        /// <summary>
        /// Gets the service locator based on the given configuration source and name.
        /// </summary>
        /// <param name="configurationSource">The configuration source.</param>
        /// <param name="name">The configuration name of service locator.</param>
        /// <returns>The servie locator.</returns>
        public  IServiceLocator GetServiceLocator(IConfigurationSource configurationSource, string name = "")
        {
            Guard.ArgumentNotNull(configurationSource, "configurationSource");
            IUnityContainer container = unityContainerAccessor(configurationSource);
            if (string.IsNullOrEmpty(name))
            {
                return container.Resolve<IServiceLocator>();
            }
            else
            {
                return container.Resolve<IServiceLocator>(name);
            }
        }

        /// <summary>
        /// Gets the service locator based on the given name.
        /// </summary>
        /// <param name="name">The configuration name of service locator.</param>
        /// <returns>The servie locator.</returns>
        public IServiceLocator GetServiceLocator(string name = "")
        {
            return GetServiceLocator(ConfigurationSourceManager.Current.ConfigurationSource, name);
        }
    }
}